-- ======================================================================
-- Hell Lua Bus
-- 
-- Copyright 2012-2013Hell-Prototypes
--
-- http://www.hellprototypes.com/
--
-- This is free software, licensed under the terms of the GNU General
-- Public License as published by the Free Software Foundation.
-- ======================================================================
hlb    = require('libhlb')
printf = require "mod_print"
iHEX   = require "mod_iHEX"

local pgm_hex_file = "Hell_uFPGA_Bootloader.hex"
--local pgm_hex_file = "led_test.hex"

local PIC_PGD_PIN = 0
local PIC_PGC_PIN = 1

local byte_mask = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}

local CMD_CORE					= 0x0	-- 0000
local CMD_SFT_TABLAT			= 0x2	-- 0010
local CMD_TAB_RD				= 0x8	-- 1000
local CMD_TAB_RD_INC			= 0x9	-- 1001
local CMD_TAB_RD_DEC			= 0xA	-- 1010
local CMD_TAB_RD_PRE_INC		= 0xB	-- 1011
local CMD_TAB_WR				= 0xC	-- 1100
local CMD_TAB_WR_INC2			= 0xD	-- 1101
local CMD_TAB_WR_ST_PGM_INC2	= 0xE	-- 1110
local CMD_TAB_WR_ST_PGM			= 0xF	-- 1111

local g_ERROR_FLAG = false
-- -----------------------------------------------
function device_open()
	local ret = hlb.usb_open()
	if ret < 0 then
		print("Fail to open usb device")
		return false
	end
	
	return true
end

function device_close()
	local ret = hlb.usb_close()
	if ret < 0 then
		print("Fail to close usb device")
		return false
	end
	
	return true
end
-- -----------------------------------------------
function msdelay(ms)
    hlb.msdelay(ms)
end

-- -----------------------------------------------
function vpp_on()
	return hlb.ctrl_12V(1)
end

function vpp_off()
	return hlb.ctrl_12V(0)
end
-- -----------------------------------------------
function PGC_output()
	local ret = hlb.bit_oe(PIC_PGC_PIN, 1)
	if ret < 0 then
		print("PGC_OUT : hlb_bit_oe ret = ", ret)
	end
end
function set_PGC()
    local ret = hlb.set_bit(PIC_PGC_PIN)
	if ret < 0 then
		print("set_PGC : usbhlb_set_bit ret = ", ret)
	end
end

function clr_PGC()
    local ret = hlb.clr_bit(PIC_PGC_PIN)
	if ret < 0 then
		print("clr_PGC : usbhlb_clr_bit ret = ", ret)
	end
end
-- -----------------------------------------------
function PGD_input()
	local ret = hlb.bit_oe(PIC_PGD_PIN, 0)
	if ret < 0 then
		print("PGD_input : hlb_bit_oe ret = ", ret)
	end
end

function GET_PGD()
	return hlb.get_bit(PIC_PGD_PIN)
end
-- -----------------------------------------------
function PGD_output()
	local ret = hlb.bit_oe(PIC_PGD_PIN, 1)
	if ret < 0 then
		print("PGD_output : hlb_bit_oe ret = ", ret)
	end
end

function set_PGD()
    local ret = hlb.set_bit(PIC_PGD_PIN)
	if ret < 0 then
		print("set_PGD : usbhlb_set_bit ret = ", ret)
	end
end

function clr_PGD()
    local ret = hlb.clr_bit(PIC_PGD_PIN)
	if ret < 0 then
		print("clr_PGD : usbhlb_clr_bit ret = ", ret)
	end
end
-- -----------------------------------------------
function icsp_init()
    --vpp_off()
    msdelay(10)

	clr_PGC()
    clr_PGD()
    PGC_output()
    PGD_output()
    msdelay(10)
    vpp_on()
end

function icsp_deinit()
    msdelay(10)
	clr_PGC()
    clr_PGD()
	vpp_off()
end
-- -----------------------------------------------
function sw_send_CMD(cmd)
    for index=1, 4 do
		if bit32.btest(cmd , byte_mask[index]) then
			set_PGD()
		else
			clr_PGD()
		end
		set_PGC()
        clr_PGC()
	end
    --clr_PGD()
end
-- -----------------------------------------------
function sw_pgm_flash_CMD()
	clr_PGD()
    for i=1, 3 do
		set_PGC()
        clr_PGC()
	end
	set_PGC()
	msdelay(5) --P9A: 3ms
    clr_PGC()
	msdelay(1) --P10: 100us
end
-- -----------------------------------------------
function sw_send_8bits(data)
    for index=1, 8 do
		if bit32.btest(data , byte_mask[index]) then
			set_PGD()
		else
			clr_PGD()
		end
		set_PGC()
        clr_PGC()
	end
    --clr_PGD()
end
-- -----------------------------------------------
function sw_read_8bits()
	local rd_data = 0
	PGD_input()
	clr_PGD()
	
 	for index=1, 8 do
		set_PGC()
		--delay
		if GET_PGD() then
			rd_data = rd_data + byte_mask[index]
		end
		clr_PGC()
	end

	PGD_output()
	
	return rd_data
end
-- -----------------------------------------------
function fw_send_CMD(cmd)
    local ret = hlb.icsp_write(cmd, 4)
	if ret < 0 then
		print("fw_send_CMD() ret = " .. ret)
		return false
	end
	return true
end
-- -----------------------------------------------
function fw_send_8bits(data)
	local ret = hlb.icsp_write(data, 8)
	if ret < 0 then
		print("fw_send_CMD() ret = " .. ret)
		return false
	end
	return true
end
-- -----------------------------------------------
function fw_read_8bits()
	return hlb.icsp_read()
end
-- -----------------------------------------------
function ll_write_sequence(cmd_4b, payload, pl_wait)
	local H = bit32.band(bit32.rshift(payload, 8), 0xFF)
    local L = bit32.band(payload, 0xFF)

    if not fw_send_CMD(cmd_4b) then
		g_ERROR_FLAG = true
		return
	end
	if pl_wait == true then
		msdelay(10)
	end
	if not fw_send_8bits(L) then
		g_ERROR_FLAG = true
		return
	end
	if not fw_send_8bits(H) then
		g_ERROR_FLAG = true
	end
end
-- -----------------------------------------------
function ll_read_sequence(cmd_4b)
	if not fw_send_CMD(cmd_4b) then
		g_ERROR_FLAG = true
		return
	end
	if not fw_send_8bits(0) then
		g_ERROR_FLAG = true
	end
	return fw_read_8bits()
end
-- -----------------------------------------------
function set_table_pointer(TBLPTR)
	local MOVLW = 0x0E00
	local SET_TBLPTRU = bit32.band(bit32.rshift(TBLPTR, 16), 0xFF) + MOVLW
	local SET_TBLPTRH = bit32.band(bit32.rshift(TBLPTR, 8),  0xFF) + MOVLW
	local SET_TBLPTRL = bit32.band(TBLPTR, 0xFF) + MOVLW

	ll_write_sequence(CMD_CORE, SET_TBLPTRU)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, 0x6EF8)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, SET_TBLPTRH)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, 0x6EF7)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, SET_TBLPTRL)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, 0x6EF6)
end
-- -----------------------------------------------
function icsp_reading_device(start_addr, length, print_progress)
	local rd_tab = {}
	local percent = 0
	local last_percent = -1
	local feed = 0

    set_table_pointer(start_addr)
	if g_ERROR_FLAG then return end
	
	for i=1, length do
		rd_tab[i] = ll_read_sequence(CMD_TAB_RD_INC)
		if g_ERROR_FLAG then return end

		if print_progress == true then
			percent = math.floor((100*i)/length)
			if percent ~= last_percent then
				last_percent = percent
				io.write(string.format("%%%02d,", percent))
				feed = feed + 1
				if feed%16 == 0 then
					print("")
				end
			end
		end
	end
	print("")

	return rd_tab
end
-- -----------------------------------------------
function icsp_chip_erase()
	set_table_pointer(0x3C0005)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_TAB_WR, 0x0F0F)
	if g_ERROR_FLAG then return end
	set_table_pointer(0x3C0004)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_TAB_WR, 0x8F8F)
	if g_ERROR_FLAG then return end
	
	ll_write_sequence(CMD_CORE, 0x0000) -- NOP
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, 0x0000, true) -- Hold PGD low until erase completes.
end
-- -----------------------------------------------
function icsp_page_write(addr, tab_wr_data)
	ll_write_sequence(CMD_CORE, 0x8EA6)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, 0x9CA6)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, 0x84A6)
	if g_ERROR_FLAG then return end
	
	set_table_pointer(addr)

	local wr_data = tab_wr_data
	if #wr_data < 16 then
		for i=#wr_data + 1, 16 do
			wr_data[i] = 0xFF
		end
	end

	local payload = 0
	for i=1, 7 do
		payload = bit32.lshift(wr_data[i*2], 8) + wr_data[i*2 - 1]
		ll_write_sequence(CMD_TAB_WR_INC2, payload)
		if g_ERROR_FLAG then return end
	end
	payload = bit32.lshift(wr_data[16], 8) + wr_data[15]
	ll_write_sequence(CMD_TAB_WR_ST_PGM, payload)
	if g_ERROR_FLAG then return end
	
	--NOP - hold PGC high for time P9 and low for time P10.
	sw_pgm_flash_CMD()
	fw_send_8bits(0)
	fw_send_8bits(0)
end
-- -----------------------------------------------
function icsp_cfg_bits_program(addr_offset, cfg_data)
	if addr_offset > 0x0D then
		g_ERROR_FLAG = true
		return
	end
	ll_write_sequence(CMD_CORE, 0x8EA6)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, 0x8CA6)
	if g_ERROR_FLAG then return end
	ll_write_sequence(CMD_CORE, 0x84A6)
	if g_ERROR_FLAG then return end
	
	set_table_pointer(0x300000 + addr_offset)
	
	local payload = 0
	if bit32.btest(addr_offset , 1) then
		payload = bit32.lshift(cfg_data, 8)
	else
		payload = cfg_data
	end
	ll_write_sequence(CMD_TAB_WR_ST_PGM, payload)
	
	--NOP - hold PGC high for time P9 and low for time P10.
	sw_pgm_flash_CMD()
	fw_send_8bits(0)
	fw_send_8bits(0)
end
-- -----------------------------------------------
function icsp_flash_program(start_addr, pgm_raw_data)
	local index = 1
	local wr_data={}
	local percent = 0
	local last_percent = -1
	local feed = 0
	while index < #pgm_raw_data do
		for i=0, 15 do
			if i+index <= #pgm_raw_data then
				wr_data[i+1] = pgm_raw_data[i+index]
			else
				wr_data[i+1] = 0xFF;
			end
			
		end
		icsp_page_write(start_addr + index - 1, wr_data)
		if g_ERROR_FLAG then return end
		
		percent = math.floor((100*index)/#pgm_raw_data)
		if percent ~= last_percent then
			last_percent = percent
			io.write(string.format("%%%02d,", percent))
			feed = feed + 1
			if feed%16 == 0 then
				print("")
			end
		end
		index = index + 16
	end
	print("")
end
-- -----------------------------------------------
function exit_process()
	icsp_deinit()
	device_close()
end
-- -----------------------------------------------

f=io.open(pgm_hex_file,"r")
if f~=nil then
	io.close(f)
else
	print("Error: Program file not exist, file name: " .. pgm_hex_file)
	return
end

local pgm_start_addr
local pgm_raw_data
pgm_start_addr, pgm_raw_data = iHEX.Read_File(pgm_hex_file)
if type(pgm_raw_data) == "table" then
	print(string.format("\r\nFW start address = 0x%04x", pgm_start_addr))
	printf.table(pgm_raw_data, true)
else
	print("Error: fail to read hex file.")
	return
end

if not device_open() then
	print("device_open fail")
	return
end

-- ======
icsp_init()

-- ======
g_ERROR_FLAG = false
print("> Ready to work, connect the device then press any key to continue ..")
io.read()
-- ======
print("\r\n*** Read Devide ID")
ret_tab = icsp_reading_device(0x3FFFFE, 2)
if g_ERROR_FLAG then 
	print("*** Read Devide ID FAIL")
	exit_process()
	return
end
printf.table(ret_tab)
if #ret_tab ~= 2 then
    print("*** Devide ID Read Error")
    exit_process()
	return
else
    if ret_tab[2] ~= 0x47 then
        print("*** Devide ID Error")
	   exit_process()
       return
    end
end

-- ======
print("\r\n*** Chip Erase")
icsp_chip_erase()
if g_ERROR_FLAG then 
	print("*** Chip Erase FAIL")
	exit_process()
	return
end

-- ======
print("\r\n*** Flash program")
icsp_flash_program(0, pgm_raw_data)
if g_ERROR_FLAG then 
	print("*** Flash program FAIL")
	exit_process()
	return
else
	print("\r\n*** Flash program DONE")
end
---[[ ======
print("\r\n*** Read from Flash")
ret_tab = icsp_reading_device(0, #pgm_raw_data, true)
if g_ERROR_FLAG then
	print("*** Read from Flash address FAIL")
	exit_process()
	return
end
printf.table(ret_tab)
-- ======
if #ret_tab ~= #pgm_raw_data then
    exit_process()
    print("\r\n*** Data Verify FAIL")
    return
else
    for i=1, #ret_tab do
        if ret_tab[i] ~= pgm_raw_data[i] then
            exit_process()
            print("\r\n*** Data Verify FAIL")
            return
        end
    end
end
print("\r\n*** Data Verify OK")

--]]
-- ======
print("\r\n*** Write Configuration Bits")
icsp_cfg_bits_program(0x01, 0x32)
icsp_cfg_bits_program(0x03, 0x1E)
icsp_cfg_bits_program(0x05, 0x08)
icsp_cfg_bits_program(0x06, 0x81)
icsp_cfg_bits_program(0x08, 0x00)
icsp_cfg_bits_program(0x09, 0x00)
icsp_cfg_bits_program(0x0B, 0x80)

-- ======
print("\r\n*** Read User ID")
ret_tab = icsp_reading_device(0x200000, 8)
if g_ERROR_FLAG then 
	print("*** Read User ID FAIL")
	exit_process()
	return
end
printf.table(ret_tab)

-- ======
print("\r\n*** Read Configuration Bits")
ret_tab = icsp_reading_device(0x300000, 14)
if g_ERROR_FLAG then 
	print("*** Read Configuration Bits FAIL")
	exit_process()
	return
end
printf.table(ret_tab)

-- ======
exit_process()